/* 
 * File:   atribuicao.cpp
 * Author: Henrique
 * 
 * Created on 5 de Abril de 2010, 15:04
 */

#include <iostream>
#include <iosfwd>
#include <string>
#include <bits/basic_string.h>

#include "atribuicao.h"

atribuicao::atribuicao() {
}

atribuicao::atribuicao(char c, double var[]) {
    memoria = var;
    charVar = c;
}

atribuicao::atribuicao(const atribuicao& orig) {
}

atribuicao::~atribuicao() {
}

double* atribuicao::getMem() {

    return this->memoria;
}

std::vector<std::string> atribuicao::tok(std::string str, char* delimiters) {
    // Skip delimiters at beginning.
    std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    std::string::size_type pos = str.find_first_of(delimiters, lastPos);

    std::vector<std::string> tokens;
    while (std::string::npos != pos || std::string::npos != lastPos) {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
    return tokens;
}

template <typename T>
std::string atribuicao::to_string(T const &t) {
    std::ostringstream oss;
    oss << t;
    return oss.str();
}

std::string& atribuicao::replaceAll(std::string& context, const std::string& from, const std::string& to) {
    size_t lookHere = 0;
    size_t foundHere;
    while ((foundHere = context.find(from, lookHere)) != std::string::npos) {
        context.replace(foundHere, from.size(), to);
        lookHere = foundHere + to.size();
    }
    return context;
} ///:~

std::string atribuicao::resolveOp(std::string str, char* ch) {
    double value = 0.0;
    std::vector<std::string> exp = tok(str, ch);
    int div = exp.size() / 2;
    double value1 = 0.0;
    double value2 = 0.0;
    std::string s1 = exp.at(0);
    std::string s2 = exp.at(1);
    std::string temp1 = std::string();
    for (int i = s1.size() - 1; i >= 0; i--) {

        char c1 = s1.at(i);
        if (isdigit(c1)) {

            temp1.insert(temp1.begin(), c1);
        } else {
            value1 = strtod(temp1.c_str(), NULL);
            break;
        }
    }
    std::string temp2 = std::string();
    for (int i = 0; i < s2.size(); i++) {

        char c2 = s2.at(i);
        if (isdigit(c2)) {
            temp2.push_back(c2);
        } else {

            value2 = strtod(temp2.c_str(), NULL);
            break;
        }
    }
    value1 = strtod(temp1.c_str(), NULL);

    value2 = strtod(temp2.c_str(), NULL);

    if (ch == "/") {
        value = value1 / value2;
    }
    if (ch == "*") {
        value = value1 * value2;
    }
    if (ch == "+") {
        value = value1 + value2;
    }
    if (ch == "-") {
        value = value1 - value2;
    }
    if (ch == "<") {
        value = value1 < value2;
    }
    if (ch == "<=") {
        value = value1 <= value2;
    }
    if (ch == ">") {
        value = value1 > value2;
    }
    if (ch == ">=") {
        value = value1 >= value2;
    }
    if (ch == "=") {
        value = value1 == value2;
    }
    if (ch == "<>") {
        value = value1 != value2;
    }
    if (ch == "#") {
        value = value1 && value2;
    }
    if (ch == "|") {
        value = value1 || value2;
    }

   
    int pos1inicial = s1.size() - 1;
    int pos1final = 0;
    int pos2inicial = 0;
    int pos2final = s2.size() - 1;
    for (int i = s1.size() - 1; i >= 0; i--) {

        char c1 = s1.at(i);
        if (isdigit(c1)) {

        } else {
            pos1final = i - 1;
            break;
        }
    }

    for (int i = 0; i < s2.size(); i++) {

        char c2 = s2.at(i);
        if (isdigit(c2)) {

        } else {
            pos2final = i - 1;
            break;
        }
    }

    std::string strtemp;
 
    for (int i = exp.at(0).size()-1; i >= 0; i--) {
        char ch = exp.at(0).at(i);
        if(!isdigit(ch))
        {
            for (int j = 0; j <= i; j++) {

                strtemp.append(1, exp.at(0).at(j));
            }
            break;
        }
    }
    strtemp.append(to_string(value));

    //std::cout << exp.at(0) << std::endl; std::cout << exp.at(1) << std::endl;
    exp.at(1).replace(pos2inicial, pos2final - pos2inicial + 1, "");
    //std::cout << exp.at(1) << std::endl;
    std::string saida;

    saida.append(strtemp);
    saida.append(exp.at(1));
    for (int i = 2; i < exp.size(); i++) {
        saida.append(ch);
        saida.append(exp.at(i));

    }

    //std::cout << saida << std::endl;


    return saida;
}

std::string atribuicao::resolve(std::string str) {
    double value = 0.0;


    std::string s = str;

    while (s.find("*", 0) != std::string::npos) {//mul

        s = resolveOp(s, "*");
    }
    while (s.find("/", 0) != std::string::npos) {//div
        s = resolveOp(s, "/");
    }
    while (s.find("+", 0) != std::string::npos) {//add
        s = resolveOp(s, "+");
        std::string::size_type pos = s.find("+");
        pos = s.find("+", pos+1);
        if (pos  == std::string::npos)
            break;

    }
    while (s.find("-", 0) != std::string::npos) {//sub
        s = resolveOp(s, "-");
        std::string::size_type pos = s.find("-");
        pos = s.find("-", pos+1);
        if (pos  == std::string::npos)
            break;


    }
    while (s.find(">", 0) != std::string::npos) {//maior que
        s = resolveOp(s, ">");
    }
    while (s.find("<", 0) != std::string::npos) {//menor que
        s = resolveOp(s, "<");
    }
    while (s.find(">=", 0) != std::string::npos) {//maior ou igual que
        s = resolveOp(s, ">=");
    }
    while (s.find("<=", 0) != std::string::npos) {//menor ou igual que
        s = resolveOp(s, "<=");
    }
    while (s.find("<>", 0) != std::string::npos) {//dwhileerente
        s = resolveOp(s, "<>");
    }
    while (s.find("=", 0) != std::string::npos) {//igu
        s = resolveOp(s, "=");
    }
    while (s.find("#", 0) != std::string::npos) {//and logico
        s = resolveOp(s, "#");
    }
    while (s.find("|", 0) != std::string::npos) {//or logico
        s = resolveOp(s, "|");
    }
    return s;
}

std::string atribuicao::abreParentesis(std::string str, int pos, int fim) {

    double valueTemp = 0.0;
    double value = 0.0;
    if (str.find('(', 1) == 0) {
        for (int i = pos; i <= fim;) {

            char c = str.at(i);
            if (c == '(') {
                int posFim = 0;
                int pilha = 1;
                for (int j = 0; j <= fim; j++) {
                    char d = str.at(j);
                    if (d == '(')
                        pilha++;
                    if (d == ')')
                        pilha--;
                    if (pilha == 0) {
                        posFim = j;
                        valueTemp = strtod(abreParentesis(str, i + 1, j - 1).c_str(), NULL);
                        std::string s = str.substr(i, j - i + 1);
                        std::string s2 = to_string(valueTemp);
                        str.replace(i, j - i + 1, s2);
                        i = i + j - i;
                        break;
                    }
                }
                i++;
            } else {

                while (isdigit(c)) {
                    str.append(1, c);
                    i++;
                    c = str.at(i);
                }

            }
        }
    } else {
        value = strtod(resolve(str).c_str(), NULL);
    }
    memoria[(int)charVar-(int)'a'] = value;
    std::string saida = to_string(value);
    return saida;

}

double atribuicao::processamentoAtribuicao(std::string& comando) {

    double value = 0.0;
    std::string::size_type posicao = comando.find_first_of(":");
    std::string strAtribuicao = comando.substr(posicao + 2);
    std::vector<char> tokenizer;
    replaceAll(strAtribuicao, "sqrt", "@");
    replaceAll(strAtribuicao, "and", "#");
    replaceAll(strAtribuicao, "not", "!");
    replaceAll(strAtribuicao, "or", "|");
    for (int i = 0; i < strAtribuicao.size(); i++) {
        tokenizer.push_back(strAtribuicao.at(i));
    }
    std::string stringTokenizer = "";
    for (int i = 0; i < strAtribuicao.size(); i++) {
        char c = strAtribuicao.at(i);
        if (c == ';')
            break;
        if (c != ' ') {
            if (isalpha(c) || c == '@' || c == '!') {
                if (isalpha(c) && c != '@')//se variavel
                {
                    double val = memoria[(int) c - (int) 'a'];
                    stringTokenizer.append(to_string(val));
                }
                if (c == '@')//resolve sqrt
                {
                    std::string::size_type posInicial = strAtribuicao.find_first_of("(", i) + 1;
                    std::string str = strAtribuicao.substr(posInicial);
                    std::string::size_type posFinal = str.find_first_of(")");
                    str = str.substr(0, posFinal);
                    if (str.size() == 1) {
                        char char1 = str.at(0);
                        if (isalpha(char1)) {
                            double val = memoria[((int) char1) - (int) 'a'];
                            val = sqrt(val); //val ao quadrado
                            stringTokenizer.append(to_string(val));

                        } else {
                            double val = (double) c;
                            val = val*val; //val ao quadrado
                            stringTokenizer.append(to_string(val));
                        }
                    } else {
                        double val = (double) c;
                        val = val*val; //val ao quadrado
                        stringTokenizer.append(to_string(val));
                    }
                    i = i + str.size() + 2;
                }
                if (c == '!') {//resollve not
                    char var = strAtribuicao.at(i + 1);
                    double valVar = (double) var;
                    if (valVar == 0)
                        valVar = 1.0;
                    else
                        valVar = 0.0;
                    stringTokenizer.append(to_string(valVar));
                }
            } else//se sinais ou numeros
            {
                stringTokenizer.append(to_string(c));
            }
        }
    }
  //std::cout << stringTokenizer << std::endl;
    std::string valuefinal;

    valuefinal = abreParentesis(stringTokenizer, 0, stringTokenizer.size() - 1);
    //std::cout << strtod(valuefinal.c_str(),NULL) << std::endl;
    return strtod(valuefinal.c_str(),NULL);
}
